﻿using System;
using System.Reflection;
using System.Text;
using System.Web.Mvc;
using System.Web;
using System.Web.UI;
using System.IO;
using System.Web.Caching;
using System.Web.Routing;
namespace LoveMusic
{
    public static class HtmlHelperExtensions
    {

        public static string Image(this HtmlHelper helper,
                                    string url,
                                    string altText,
                                    object htmlAttributes)
        {
            TagBuilder builder = new TagBuilder("img");
            builder.Attributes.Add("src", url);
            builder.Attributes.Add("alt", altText);
            builder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
            return builder.ToString(TagRenderMode.SelfClosing);
        }
    }


    public static class CacheExtensions
    {
        public static string Substitution<T>(this HtmlHelper helper, string method)
        {
            // Check input
            if (typeof(T).GetMethod(method, BindingFlags.Static | BindingFlags.Public) == null)
            {
                throw new ArgumentException(
                    string.Format("Type {0} does not implement a static method named {1}.",
                        typeof(T).FullName, method),
                            "method");
            }
            // Write output
            StringBuilder sb = new StringBuilder();
            sb.Append("<!--");
            sb.Append("SUBSTITUTION:");
            sb.Append(typeof(T).FullName);
            sb.Append(":");
            sb.Append(method);
            sb.Append("-->");
            return sb.ToString();
        }

        public delegate string MvcCacheCallback(HttpContextBase context);

        public static object Substitute2(this HtmlHelper html, MvcCacheCallback cb)
        {
            html.ViewContext.HttpContext.Response.WriteSubstitution(
                c => HttpUtility.HtmlEncode(
                    cb(new HttpContextWrapper(c))
                ));
            return null;
        }
    }

    public partial class CacheTesting : BaseController
    {
        public static string GetUserName(ControllerContext context)
        {
            return "nesto";
        }
    }

    public class ActionOutputCacheAttribute : ActionFilterAttribute
    {
        // This hack is optional; I'll explain it later in the blog post
        private static MethodInfo _switchWriterMethod = typeof(HttpResponse).GetMethod("SwitchWriter", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

        public ActionOutputCacheAttribute(int cacheDuration)
        {
            _cacheDuration = cacheDuration;
        }

        private int _cacheDuration;
        private TextWriter _originalWriter;
        private string _cacheKey;

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            _cacheKey = ComputeCacheKey(filterContext);
            string cachedOutput = (string)filterContext.HttpContext.Cache[_cacheKey];
            if (cachedOutput != null)
                filterContext.Result = new ContentResult { Content = cachedOutput };
            else
                _originalWriter = (TextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { new HtmlTextWriter(new StringWriter()) });
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            if (_originalWriter != null) // Must complete the caching
            {
                HtmlTextWriter cacheWriter = (HtmlTextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { _originalWriter });
                string textWritten = ((StringWriter)cacheWriter.InnerWriter).ToString();
                filterContext.HttpContext.Response.Write(textWritten);

                filterContext.HttpContext.Cache.Add(_cacheKey, textWritten, null, DateTime.Now.AddSeconds(_cacheDuration), Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
            }
        }

        private string ComputeCacheKey(ActionExecutingContext filterContext)
        {
            var keyBuilder = new StringBuilder();
            foreach (var pair in filterContext.RouteData.Values)
                keyBuilder.AppendFormat("rd{0}_{1}_", pair.Key.GetHashCode(), pair.Value.GetHashCode());
            foreach (var pair in filterContext.ActionParameters)
                keyBuilder.AppendFormat("ap{0}_{1}_", pair.Key.GetHashCode(), pair.Value.GetHashCode());
            return keyBuilder.ToString();
        }
    }
}